package com.quickask.dao;

import com.quickask.common.Pager;
import com.quickask.entity.AsknowQuestionSet;
import com.quickask.exception.HibernateDaoSupportException;
import com.quickask.idao.IBaseDao;
import org.apache.log4j.Logger;
import org.hibernate.*;
import org.hibernate.jdbc.Work;
import org.hibernate.transform.ResultTransformer;
import org.springframework.dao.DataAccessException;
import com.quickask.common.RowMapper;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.sql.*;
import java.util.*;
import java.util.Date;

/**
 * Created by moonlight on 2015/9/6.
 */
public class BaseDao<T, PK extends Serializable> implements IBaseDao<T, PK> {

    protected Logger log = Logger.getLogger(this.getClass());
    private SessionFactory sessionFactory;
    private boolean isCacheQueries = false;
    private CacheMode cacheMode = CacheMode.NORMAL;
    protected Class<T> entityClass;

    public BaseDao() {
    }

    protected Class getEntityClass() {
        if (entityClass == null) {
            entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return entityClass;
    }

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public boolean isCacheQueries() {
        return isCacheQueries;
    }

    public void setCacheQueries(boolean isCacheQueries) {
        this.isCacheQueries = isCacheQueries;
    }

    public CacheMode getCacheMode() {
        return cacheMode;
    }

    public void setCacheMode(CacheMode cacheMode) {
        this.cacheMode = cacheMode;
    }

    /**
     * 设置每行批处理参数
     *
     * @param ps
     * @param pos  ?占位符索引，从0开始
     * @param data
     * @throws SQLException
     * @see [类、类#方法、类#成员]
     */
    private void setParameter(PreparedStatement ps, int pos, Object data) throws SQLException {
        if (data == null) {
            ps.setNull(pos + 1, Types.VARCHAR);
            return;
        }
        Class dataCls = data.getClass();
        if (String.class.equals(dataCls)) {
            ps.setString(pos + 1, (String) data);
        } else if (boolean.class.equals(dataCls)) {
            ps.setBoolean(pos + 1, ((Boolean) data));
        } else if (int.class.equals(dataCls)) {
            ps.setInt(pos + 1, (Integer) data);
        } else if (double.class.equals(dataCls)) {
            ps.setDouble(pos + 1, (Double) data);
        } else if (Date.class.equals(dataCls)) {
            Date val = (Date) data;
            ps.setTimestamp(pos + 1, new Timestamp(val.getTime()));
        } else if (BigDecimal.class.equals(dataCls)) {
            ps.setBigDecimal(pos + 1, (BigDecimal) data);
        } else {
            ps.setObject(pos + 1, data);
        }
    }

    /**
     * <保存实体>
     * <完整保存实体>
     *
     * @param entity 实体参数
     * @see com.quickask.idao.IBaseDao#save(java.lang.Object)
     */
    @Override
    public void save(T entity) {
        this.getSession().save(entity);
    }

    /**
     * <保存或者更新实体>
     *
     * @param entity 实体
     * @see com.quickask.idao.IBaseDao#saveOrUpdate(java.lang.Object)
     */
    @Override
    public void saveOrUpdate(T entity) {
        this.getSession().saveOrUpdate(entity);
    }

    /**
     * <load>
     * <加载实体的load方法>
     *
     * @param id 实体的id
     * @return 查询出来的实体
     * @see com.quickask.idao.IBaseDao#load(java.io.Serializable)
     */
    @Override
    public T load(PK id) {
        T entity = (T) this.getSession().load(getEntityClass(),id);
        return entity;
    }

    /**
     * <get>
     * <查找的get方法>
     *
     * @param id 实体的id
     * @return 查询出来的实体
     * @see com.quickask.idao.IBaseDao#get(java.io.Serializable)
     */
    @Override
    public T get(PK id) {
        T entity = (T) this.getSession().get(getEntityClass(), id);
        return entity;
    }

    /**
     * <contains>
     *
     * @param entity 实体
     * @return 是否包含
     * @see com.quickask.idao.IBaseDao#contains(java.lang.Object)
     */
    @Override
    public boolean contains(T entity) {
        return this.getSession().contains(entity);
    }

    /**
     * <delete>
     * <删除表中的t数据>
     *
     * @param entity 实体
     * @see com.quickask.idao.IBaseDao#delete(java.lang.Object)
     */
    @Override
    public void delete(T entity) {
        this.getSession().delete(entity);
    }

    /**
     * <根据ID删除数据>
     *
     * @param id 实体id
     * @return 是否删除成功
     * @see com.quickask.idao.IBaseDao (java.io.Serializable)
     */
    @Override
    public boolean deleteById(PK id) {
        T entity = get(id);
        if (entity == null) {
            return false;
        }
        delete(entity);
        return true;
    }

    /**
     * <删除所有>
     *
     * @param entities 实体的Collection集合
     * @see com.quickask.idao.IBaseDao#deleteAll(java.util.Collection)
     */
    @Override
    public void deleteAll(Collection<T> entities) {
        for (Object entity : entities) {
            this.getSession().delete(entity);
        }
    }

    /**
     * <执行Hql语句>
     *
     * @param hqlString hql
     * @param values    不定参数数组
     * @see com.quickask.idao.IBaseDao#queryHql(java.lang.String, java.lang.Object[])
     */
    @Override
    public int queryHql(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.executeUpdate();
    }

    public int queryHql(String hqlString, Map<String, Object> properties) {
        Query query = this.getSession().createQuery(hqlString);
        if (properties != null && !properties.isEmpty()) {
            query.setProperties(properties);
        }
        return query.executeUpdate();
    }

    /**
     * <执行Sql语句>
     *
     * @param sqlString sql
     * @param values    不定参数数组
     * @see com.quickask.idao.IBaseDao#querySql(java.lang.String, java.lang.Object[])
     */
    @Override
    public void querySql(String sqlString, Object... values) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        query.executeUpdate();
    }

    public void querySql(String sqlString, Map<String, Object> properties) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (properties != null && !properties.isEmpty()) {
            query.setProperties(properties);
        }
        query.executeUpdate();
    }

    /**
     * <根据HQL语句查找唯一实体>
     *
     * @param hqlString HQL语句
     * @param values    不定参数的Object数组
     * @return 查询实体
     * @see com.quickask.idao.IBaseDao#getByHQL(java.lang.String, java.lang.Object[])
     */
    @Override
    public T getByHQL(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return (T) query.uniqueResult();
    }

    /**
     * <根据SQL语句查找唯一实体>
     *
     * @param sqlString SQL语句
     * @param values    不定参数的Object数组
     * @return 查询实体
     * @see com.quickask.idao.IBaseDao#getBySQL(java.lang.String, java.lang.Object[])
     */
    @Override
    public T getBySQL(String sqlString, Object... values) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[0]);
            }
        }
        return (T) query.uniqueResult();
    }

    /**
     * <根据HQL语句，得到对应的list>
     *
     * @param hqlString HQL语句
     * @param values    不定参数的Object数组
     * @return 查询多个实体的List集合
     * @see com.quickask.idao.IBaseDao#getListByHQL(java.lang.String, java.lang.Object[])
     */
    @Override
    public List<T> getListByHQL(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.list();
    }

    /**
     * <根据HQL语句，得到对应的list>
     *
     * @param hqlString HQL语句
     * @param properties    不定参数的Map
     * @return 查询多个实体的List集合
     * @see com.quickask.idao.IBaseDao#getListByHQL(java.lang.String, java.lang.Object[])
     */
    public List<T> getListByHQL(String hqlString, Map<String, Object> properties) {
        Query query = this.getSession().createQuery(hqlString);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (null!=properties && !properties.isEmpty()) {
            query.setProperties(properties);
        }
        return query.list();
    }

    /**
     * <根据SQL语句，得到对应的list>
     *
     * @param sqlString HQL语句
     * @param values    不定参数的Object数组
     * @return 查询多个实体的List集合
     * @see com.quickask.idao.IBaseDao#getListBySQL(java.lang.String, java.lang.Object[])
     */
    @Override
    public List<T> getListBySQL(String sqlString, Object... values) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.list();
    }

    /**
     * <根据SQL语句，得到对应的list>
     *
     * @param sqlString HQL语句
     * @param properties    不定参数的Map
     * @return 查询多个实体的List集合
     * @see com.quickask.idao.IBaseDao#getListBySQL(java.lang.String, java.lang.Object[])
     */
    public List<T> getListBySQL(String sqlString, Map<String, Object> properties) {
        Query query = this.getSession().createSQLQuery(sqlString);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (null!=properties && !properties.isEmpty()) {
            query.setProperties(properties);
        }
        return query.list();
    }

    /**
     * 由sql语句得到List
     *
     * @param sql
     * @param map
     * @param values
     * @return List
     * @see com.quickask.idao.IBaseDao#findListBySql(java.lang.String, com.quickask.common.RowMapper, java.lang.Object[])
     */
    @Override
    public List findListBySql(final String sql, final RowMapper map, final Object... values) {
        final List list = new ArrayList();
        // 执行JDBC的数据批量保存
        Work jdbcWork = new Work() {
            @Override
            public void execute(Connection connection) throws SQLException {
                PreparedStatement ps = null;
                ResultSet rs = null;
                try {
                    ps = connection.prepareStatement(sql);
                    for (int i = 0; i < values.length; i++) {
                        setParameter(ps, i, values[i]);
                    }
                    rs = ps.executeQuery();
                    int index = 0;
                    while (rs.next()) {
                        Object obj = map.mapRow(rs, index++);
                        list.add(obj);
                    }
                } finally {
                    if (rs != null) {
                        rs.close();
                    }
                    if (ps != null) {
                        ps.close();
                    }
                }
            }
        };
        this.getSession().doWork(jdbcWork);
        return list;
    }

    /**
     * 刷新实体
     *
     * @param entity 实体
     * @see com.quickask.idao.IBaseDao#refresh(java.lang.Object)
     */
    @Override
    public void refresh(T entity) {
        this.getSession().refresh(entity);
    }

    /**
     * 更新实体
     *
     * @param entity 实体
     * @see com.quickask.idao.IBaseDao#update(java.lang.Object)
     */
    @Override
    public void update(T entity) {
        this.getSession().update(entity);
    }

    @Override
    public boolean exists(PK id) {
        return get(id) != null;
    }

    /**
     * <根据HQL得到记录数>
     *
     * @param hqlString HQL语句
     * @param values    不定参数的Object数组
     * @return 记录总数
     * @see com.quickask.idao.IBaseDao#countByHql(java.lang.String, java.lang.Object[])
     */
    @Override
    public Long countByHql(String hqlString, Object... values) {
        Query query = this.getSession().createQuery(hqlString);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return (Long) query.uniqueResult();
    }

    public Long countByHql(String hqlString, Map<String, Object> properties) {
        Query query = this.getSession().createQuery(hqlString);
        if (properties != null) {
            query.setProperties(properties);
        }
        return (Long) query.uniqueResult();
    }

    /**
     * The <code>getRecordCount()</code> method is used for getting the total
     * count of records.
     *
     * @return PK return total of record counts
     * @throws HibernateDaoSupportException Throws HibernateDaoSupportException when accessing and
     *                                      manipulating database happen exception.
     */
    @Override
    public Integer getRecordCount(Class<T> clazz) throws HibernateDaoSupportException {
        String hql = "SELECT COUNT(*) FROM " + clazz.getName();
        Integer count = null;
        try {
            Query query = this.getSession().createQuery(hql);
            count = (Integer) query.uniqueResult();
            log.debug("Execute getRecordCount method is successful!");
        } catch (DataAccessException e) {
            log.error("Error getting count:{}", e);
            throw new HibernateDaoSupportException("Error getting count for ("
                    + this.getClass().getName() + "):" + e);
        }
        return count;
    }

    /**
     * get count with select hql and param map
     *
     * @param selectHql  according select hql
     * @param properties according param map
     * @return count of hql
     * @throws HibernateDaoSupportException when accessing and manipulating database happen exception
     */
    public int getRecordCount(String selectHql, Map<String, Object> properties)
            throws HibernateDaoSupportException {
        String countHql = getCountHql(selectHql);
        Query query = this.getSession().createQuery(selectHql);
        if (!properties.isEmpty()) {
            query.setProperties(properties);
        }
        return (Integer) query.uniqueResult();
    }

    /**
     * get count hql with select hql
     *
     * @param hql select hql
     * @return count hql
     */
    protected String getCountHql(String hql) {
        if (hql.isEmpty()) {
            log.error("Error getHqlBean because hql is empty");
            return "";
        }
        return "select count(*) " + hql.substring(hql.indexOf("from"))
                .replace("fetch", "")
                .replaceAll(
                        "\\s((?mi)(left|right|inner)?\\s+join)\\s+[^\\s]*Set\\b",
                        " ").split("order by")[0];
    }

    /**
     * <HQL分页查询>
     *
     * @param hql      HQL语句
     * @param countHql 查询记录条数的HQL语句
     * @param currentPage   下一页
     * @param pageSize 一页总条数
     * @param values   不定Object数组参数
     * @return Pager的封装类，里面包含了页码的信息以及查询的数据List集合
     * @see com.quickask.idao.IBaseDao#findPagerByFetchedHql(java.lang.String, java.lang.String, int, int, java.lang.Object[])
     */
    @Override
    public Pager<T> findPagerByFetchedHql(String hql, String countHql, int currentPage, int pageSize, Object... values) {
        currentPage = currentPage > 1 ? currentPage : 1;
        Query query = this.getSession().createQuery(hql);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        Pager<T> page = new Pager<T>();
        List<T> list = null;
        page.setCurrentPage(currentPage);
        //page.setTotal(totalRecords);
        page.setPageSize(pageSize);
        if (countHql == null) {
            ScrollableResults results = query.scroll();
            results.last();
            page.setTotal(results.getRowNumber() + 1);
        } else {
            page.setTotal(countByHql(countHql, values).intValue());
        }
        list = query.setFirstResult((currentPage - 1) * pageSize).setMaxResults(pageSize).list();
        if (list == null) {
            list = new ArrayList<T>();
        }
        page.setPageRecords(list);
        return page;
    }

    /**
     * <HQL分页查询>
     *
     * @param hql      HQL语句
     * @param countHql 查询记录条数的HQL语句
     * @param currentPage   下一页
     * @param pageSize 一页总条数
     * @param properties   不定Object数组参数
     * @return Pager的封装类，里面包含了页码的信息以及查询的数据List集合
     * @see com.quickask.idao.IBaseDao#findPagerByFetchedHql(java.lang.String, java.lang.String, int, int, java.lang.Object[])
     */
    public Pager<T> findPagerByFetchedHql(String hql, String countHql, int currentPage, int pageSize, Map<String, Object> properties) {
        currentPage = currentPage > 1 ? currentPage : 1;
        Query query = this.getSession().createQuery(hql);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (properties != null && !properties.isEmpty()) {
            query.setProperties(properties);
        }
        Pager<T> page = new Pager<T>();
        List<T> list = null;
        page.setCurrentPage(currentPage);
        //page.setTotal(totalRecords);
        page.setPageSize(pageSize);
        if (countHql == null) {
            ScrollableResults results = query.scroll();
            results.last();
            page.setTotal(results.getRowNumber() + 1);
        } else {
            page.setTotal(countByHql(countHql, properties).intValue());
        }
        list = query.setFirstResult((currentPage - 1) * pageSize).setMaxResults(pageSize).list();
        if (list == null) {
            list = new ArrayList<T>();
        }
        page.setPageRecords(list);
        return page;
    }


    /**
     * <SQL分页查询>
     *
     * @param sql      SQL语句
     * @param countSql 查询记录条数的HQL语句
     * @param currentPage   下一页
     * @param pageSize 一页总条数
     * @param properties   不定Object数组参数
     * @return Pager的封装类，里面包含了页码的信息以及查询的数据List集合
     * @see com.quickask.idao.IBaseDao#findPagerByFetchedHql(java.lang.String, java.lang.String, int, int, java.lang.Object[])
     */
    public Pager<T> findPagerByFetchedSql(String sql, String countSql, int currentPage, int pageSize, Map<String, Object> properties) {
        currentPage = currentPage > 1 ? currentPage : 1;
        Query query = this.getSession().createSQLQuery(sql).addEntity(getEntityClass());
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (properties != null && !properties.isEmpty()) {
            query.setProperties(properties);
        }
        Pager<T> page = new Pager<T>();
        List<T> list = null;
        page.setCurrentPage(currentPage);
        //page.setTotal(totalRecords);
        page.setPageSize(pageSize);
        if (countSql == null) {
            ScrollableResults results = query.scroll();
            results.last();
            page.setTotal(results.getRowNumber() + 1);
        } else {
            page.setTotal(countByHql(countSql, properties).intValue());
        }
        list = query.setFirstResult((currentPage - 1) * pageSize).setMaxResults(pageSize).list();
        if (list == null) {
            list = new ArrayList<T>();
        }
        page.setPageRecords(list);
        return page;
    }

    @Override
    public Pager<T> findPager(int currentPage, int pageSize, Class<T> clazz) throws HibernateDaoSupportException {
        return findPager(null, currentPage, pageSize, clazz, null);
    }

    /**
     * find page object's connection with hql and param map
     *
     * @param hql         according hql if class param is null
     * @param currentPage current page
     * @param pageSize    the number of records per page
     * @param properties  according param map
     * @return Object's connection
     * @throws HibernateDaoSupportException when accessing and manipulating database happen exception
     */
    @Override
    public Pager<T> findPager(String hql, int currentPage, int pageSize, Map<String, Object> properties) throws HibernateDaoSupportException {
        return findPager(hql, currentPage, pageSize, null, properties);
    }

    @Override
    public Pager findPagerBySql(String sql, int currentPage, int pageSize, Map<String, Object> properties) throws HibernateDaoSupportException {
        return null;
    }

    /**
     * find page object's connection with hql class and param map
     *
     * @param hql         according hql
     * @param currentPage current page
     * @param pageSize    the number of records per page
     * @param clazz       according class
     * @param properties  according param map
     * @return Object's connection
     * @throws HibernateDaoSupportException when accessing and manipulating database happen exception
     */
    public Pager<T> findPager(String hql, int currentPage, int pageSize,
                               Class<T> clazz, Map<String, Object> properties)
            throws HibernateDaoSupportException {
        log.debug("hql:{},currentPage:{},pageSize:{},clazz:{},propertie:{}" +
                converArrayToString(new Object[]{hql, currentPage, pageSize, clazz, properties}));

        currentPage = currentPage > 1? currentPage:1;
        int totalRecords = 0;
        if (clazz != null) {
            totalRecords = this.getRecordCount(clazz);//TODO long to int?
        } else {
            totalRecords = this.getRecordCount(hql, properties);
        }
        Pager<T> page = new Pager<T>();
        List<T> list = null;
        page.setTotal(totalRecords);
        page.setPageSize(pageSize);
        list = this.queryList(hql, (currentPage - 1) * pageSize, pageSize, clazz, properties);
        //直到找到有数据的页码为止
        while (list.size() == 0 && currentPage > 1) {
            currentPage = currentPage - 1;
            list = this.queryList(hql, (currentPage - 1) * pageSize, pageSize, clazz, properties);
        }
        page.setCurrentPage(currentPage);
        page.setPageRecords(list);
        return page;
    }

    /**
     * The <code>queryList(PK startRecord, PK pageSize)</code> method is query
     * objects according startRecord and pagesize're number, object type is
     * according your implements this method's define type, and implements this
     * interface abstract class must be override all method and inject entity
     * type.
     *
     * @param startRecord Where from the beginning to show this record
     * @param pageSize    The number of records per page
     * @param clazz       according class
     * @return List<T> T is your inject object's type, List is query all object
     * connection
     * @throws HibernateDaoSupportException Throws HibernateDaoSupportException when accessing and
     *                                      manipulating database happen exception.
     */
    @Override
    public List<T> queryList(int startRecord, int pageSize, Class<T> clazz) throws HibernateDaoSupportException {
        return queryList(null, startRecord, pageSize, clazz, null);
    }

    /**
     * find object's connection with hql class and param map
     *
     * @param hql         according hql if class param is null
     * @param startRecord Where from the beginning to show this record
     * @param pageSize    the number of records per page
     * @param clazz       according class
     * @param properties  according param map
     * @return Object's connection
     * @throws HibernateDaoSupportException when accessing and manipulating database happen exception
     */
    @Override
    public List<T> queryList(String hql, final int startRecord, final int pageSize, Class<T> clazz, final Map<String, Object> properties) throws HibernateDaoSupportException {
        if (hql == null && clazz == null) {
            throw new HibernateDaoSupportException(
                    "Param(#hql#) and param(#clazz#) is null");
        }
        if (clazz != null) {
            hql = "FROM " + clazz.getName();
        }
        Query query = this.getSession().createQuery(hql);
        if (isCacheQueries) {
            query.setCacheMode(cacheMode);
            query.setCacheable(true);
            isCacheQueries = false;
            cacheMode = CacheMode.NORMAL;
        } else {
            query.setCacheable(false);
        }
        if (!properties.isEmpty()) {
            query.setProperties(properties);
        }
        if (startRecord >= 0 && pageSize >= 0) {
            query.setFirstResult(startRecord).setMaxResults(pageSize);
        }
        return query.list();
    }

    /**
     * conver array to string
     *
     * @param values array value
     * @return string value
     */
    private String converArrayToString(Object[] values) {
        if (values == null) {
            return "";
        }
        StringBuffer formatValues = new StringBuffer();
        for (Object value : values) {
            formatValues.append("{" + value + "}");
        }
        return formatValues.toString();
    }

}
